// 文件名: common/pkt.c
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>

#include "pkt.h"
#include "../utils/concurrent.h"
#include "common.h"
pthread_mutex_t sonSendPkt_mutex=(pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t forwardpktToSIP_mutex=(pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;

void package_sippacket(sip_pkt_t* pkt){
  pkt->header.src_nodeID=htonl(pkt->header.src_nodeID);
  pkt->header.dest_nodeID=htonl(pkt->header.dest_nodeID);
  pkt->header.length=htons(pkt->header.length);
  pkt->header.type=htons(pkt->header.type);
}

void parse_sippacket(sip_pkt_t* pkt){
  pkt->header.src_nodeID=ntohl(pkt->header.src_nodeID);
  pkt->header.dest_nodeID=ntohl(pkt->header.dest_nodeID);
  pkt->header.length=ntohs(pkt->header.length);
  pkt->header.type=ntohs(pkt->header.type);
}

void show_sippacket(sip_pkt_t* pkt){
	char* type_str=NULL;
	switch (pkt->header.type){
		case SIP:
			type_str="SIP";
			break;
		case ROUTE_UPDATE:
			type_str="ROUTE UPDATE";
			break;
		default :{
			concurrent_printf_warning("unknown sip packet type!\n");
			return ;
		}
	}
	concurrent_printf("********************\n");
	concurrent_printf("src_nodeID:%d\n",pkt->header.src_nodeID);
	concurrent_printf("dest_nodeID:%d\n",pkt->header.dest_nodeID);
	concurrent_printf("type:%s\n",type_str);
	concurrent_printf("length:%d\n",pkt->header.length);
	concurrent_printf("********************\n");
}



// son_sendpkt()由SIP进程调用, 其作用是要求SON进程将报文发送到重叠网络中. SON进程和SIP进程通过一个本地TCP连接互连.
// 在son_sendpkt()中, 报文及其下一跳的节点ID被封装进数据结构sendpkt_arg_t, 并通过TCP连接发送给SON进程. 
// 参数son_conn是SIP进程和SON进程之间的TCP连接套接字描述符.
// 当通过SIP进程和SON进程之间的TCP连接发送数据结构sendpkt_arg_t时, 使用'!&'和'!#'作为分隔符, 按照'!& sendpkt_arg_t结构 !#'的顺序发送.
// 如果发送成功, 返回1, 否则返回-1.

//NEED_MUTEX

int son_sendpkt(int nextNodeID, sip_pkt_t* pkt, int son_conn)
{
	concurrent_printf_reminder("sip:son_send to nextNode:%d\n",nextNodeID);
	show_sippacket(pkt);

	sip_pkt_t pkt_sub;
	memcpy(&pkt_sub,pkt,sizeof(sip_pkt_t));
	package_sippacket(&pkt_sub);
	nextNodeID=htonl(nextNodeID);

	int ret=-1;
	pthread_mutex_lock(&sonSendPkt_mutex);
	ret=send_data(son_conn,(const char*)&nextNodeID,sizeof(int));
	ret=send_data(son_conn,(const char*)&pkt_sub,sizeof(sip_pkt_t));
	pthread_mutex_unlock(&sonSendPkt_mutex);
	
	return ret;
}

// son_recvpkt()函数由SIP进程调用, 其作用是接收来自SON进程的报文. 
// 参数son_conn是SIP进程和SON进程之间TCP连接的套接字描述符. 报文通过SIP进程和SON进程之间的TCP连接发送, 使用分隔符!&和!#. 
// 为了接收报文, 这个函数使用一个简单的有限状态机FSM
// PKTSTART1 -- 起点 
// PKTSTART2 -- 接收到'!', 期待'&' 
// PKTRECV -- 接收到'&', 开始接收数据
// PKTSTOP1 -- 接收到'!', 期待'#'以结束数据的接收 
// 如果成功接收报文, 返回1, 否则返回-1.


int son_recvpkt(sip_pkt_t* pkt, int son_conn){
	int ret=recv_data(son_conn,(char*)pkt,sizeof(sip_pkt_t));
	if(ret!=-1)
	{
		parse_sippacket(pkt);

		concurrent_printf_reminder("sip:son_recvpkt!\n");
		show_sippacket(pkt);
	}
	return ret;
}

// 这个函数由SON进程调用, 其作用是接收数据结构sendpkt_arg_t.
// 报文和下一跳的节点ID被封装进sendpkt_arg_t结构.
// 参数sip_conn是在SIP进程和SON进程之间的TCP连接的套接字描述符. 
// sendpkt_arg_t结构通过SIP进程和SON进程之间的TCP连接发送, 使用分隔符!&和!#. 
// 为了接收报文, 这个函数使用一个简单的有限状态机FSM
// PKTSTART1 -- 起点 
// PKTSTART2 -- 接收到'!', 期待'&' 
// PKTRECV -- 接收到'&', 开始接收数据
// PKTSTOP1 -- 接收到'!', 期待'#'以结束数据的接收
// 如果成功接收sendpkt_arg_t结构, 返回1, 否则返回-1.
int getpktToSend(sip_pkt_t* pkt, int* nextNode,int sip_conn){
	int ret=-1;
	ret=recv_data(sip_conn,(char*)nextNode,sizeof(int));
	ret=recv_data(sip_conn,(char*)pkt,sizeof(sip_pkt_t));

	if(ret!=-1)
	{
		(*nextNode)=ntohl(*nextNode);
		parse_sippacket(pkt);
		
		concurrent_printf_reminder("son:get pkt to send to node:%d\n",*nextNode);
		show_sippacket(pkt);
	}
	return ret;
}

// forwardpktToSIP()函数是在SON进程接收到来自重叠网络中其邻居的报文后被调用的. 
// SON进程调用这个函数将报文转发给SIP进程. 
// 参数sip_conn是SIP进程和SON进程之间的TCP连接的套接字描述符. 
// 报文通过SIP进程和SON进程之间的TCP连接发送, 使用分隔符!&和!#, 按照'!& 报文 !#'的顺序发送. 
// 如果报文发送成功, 返回1, 否则返回-1.
//NEED_MUTEX

int forwardpktToSIP(sip_pkt_t* pkt, int sip_conn){
	concurrent_printf_reminder("son:forward to SIP!\n");
	show_sippacket(pkt);

	sip_pkt_t pkt_sub;
	memcpy(&pkt_sub,pkt,sizeof(sip_pkt_t));
	package_sippacket(&pkt_sub);

	pthread_mutex_lock(&forwardpktToSIP_mutex);
	int ret=send_data(sip_conn,(const char*)&pkt_sub,sizeof(sip_pkt_t));
	pthread_mutex_unlock(&forwardpktToSIP_mutex);
	return ret;
}

// sendpkt()函数由SON进程调用, 其作用是将接收自SIP进程的报文发送给下一跳.
// 参数conn是到下一跳节点的TCP连接的套接字描述符.
// 报文通过SON进程和其邻居节点之间的TCP连接发送, 使用分隔符!&和!#, 按照'!& 报文 !#'的顺序发送. 
// 如果报文发送成功, 返回1, 否则返回-1.
int sendpkt(sip_pkt_t* pkt, int conn){
	concurrent_printf_reminder("son:sendpkt!\n");
	show_sippacket(pkt);

	sip_pkt_t pkt_sub;
	memcpy(&pkt_sub,pkt,sizeof(sip_pkt_t));
	package_sippacket(&pkt_sub);

	int ret=send_data(conn,(const char*)&pkt_sub,sizeof(sip_pkt_t));	
	return ret;
}

// recvpkt()函数由SON进程调用, 其作用是接收来自重叠网络中其邻居的报文.
// 参数conn是到其邻居的TCP连接的套接字描述符.
// 报文通过SON进程和其邻居之间的TCP连接发送, 使用分隔符!&和!#. 
// 为了接收报文, 这个函数使用一个简单的有限状态机FSM
// PKTSTART1 -- 起点 
// PKTSTART2 -- 接收到'!', 期待'&' 
// PKTRECV -- 接收到'&', 开始接收数据
// PKTSTOP1 -- 接收到'!', 期待'#'以结束数据的接收 
// 如果成功接收报文, 返回1, 否则返回-1.
int recvpkt(sip_pkt_t* pkt, int conn){
	int ret=recv_data(conn,(char*)pkt,sizeof(sip_pkt_t));
	if(ret!=-1)
	{
		parse_sippacket(pkt);

		concurrent_printf_reminder("son:recvpkt!\n");
		show_sippacket(pkt);
	}
	return ret;
}
